<!-- 手动上传文件组件 -->
<template>
  <div>
    <el-upload
      v-model:file-list="fileList"
      method="post"
      :multiple="props.multiple"
      :accept="props.accept"
      :limit="props.limit"
      :style="props.style"
      :disabled="props.disabled"
      :auto-upload="props.autoUpload"
      :before-upload="handleBeforeUpload"
      :http-request="handleUpload"
      :on-progress="handleProgress"
      :on-success="handleSuccess"
      :on-error="handleError"
      :on-change="handleChange"
    >
      <template #default>
        <!-- 上传文件按钮 -->
        <slot :limit="props.limit">
          <!-- <el-button type="primary" :disabled="fileList.length >= props.limit">
            {{ props.uploadBtnText }}
          </el-button> -->
        </slot>
      </template>

      <template #tip>
        <slot name="tip"></slot>
        <!-- <slot name="tip" :maxFileSize="maxFileSize">
          <div class="el-upload__tip">{{ `注：单个上传文件不超过${props.maxFileSize}M` }}</div>
        </slot> -->
      </template>

      <!-- 文件列表 -->
      <template #file="{ file, index }">
        <slot
          name="file"
          :file="file"
          :index="index"
          :download="() => handleDownload(file)"
          :remove="() => handleRemove(file)"
        >
          <div class="el-upload-list__item-info">
            <a class="el-upload-list__item-name" @click="handleDownload(file)">
              <el-icon>
                <Document />
              </el-icon>
              <span class="el-upload-list__item-file-name">{{ file.name }}</span>
              <span class="el-icon--close" @click.stop="handleRemove(file)">
                <el-icon>
                  <Close />
                </el-icon>
              </span>
            </a>
          </div>
        </slot>
      </template>
    </el-upload>

    <el-progress
      :style="{
        display: showProgress ? 'inline-flex' : 'none',
        width: '100%',
      }"
      :percentage="progressPercent"
    />
  </div>
</template>
<script lang="ts" setup>
import { UploadFile, UploadFiles, UploadProgressEvent } from 'element-plus';

import FileAPI from '@/api/system/file.api';
import { accept } from '@/utils/validate';

const props = defineProps({
  /**
   * 支持上传多个文件
   */
  multiple: {
    type: Boolean,
    default: false,
  },
  /**
   * 请求携带的额外参数
   */
  data: {
    type: Object,
    default: () => {
      return {};
    },
  },
  autoUpload: {
    type: Boolean,
    default: false,
  },
  /**
   * 上传文件的参数名
   */
  name: {
    type: String,
    default: 'file',
  },
  /**
   * 文件上传数量限制
   */
  limit: {
    type: Number,
    default: 1,
  },
  /**
   * 单个文件上传大小限制(单位MB)
   */
  maxFileSize: {
    type: Number,
    default: 10,
  },
  /**
   * 上传文件类型 默认 .xlsx
   */
  accept: {
    type: String,
    default: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  },
  /**
   * 上传按钮文本
   */
  uploadBtnText: {
    type: String,
    default: '上传文件',
  },
  onSuccess: {
    type: Function,
  },
  /**
   * 样式
   */
  style: {
    type: Object,
    default: () => {
      return {
        width: '300px',
      };
    },
  },
  disabled: {
    type: Boolean,
    default: false
  }
});

// defineModel的返回值是一个ref 子组件中修改modelValue的值，父组件也会同步更新
const modelValue = defineModel('modelValue', {
  type: String,
  required: true,
  default: '',
});

const emit = defineEmits(['update:modelValue', 'on-change', 'on-success', 'remove']);

const fileList = ref<any>([]);

const showProgress = ref(false);
const progressPercent = ref(0);
// 监听 modelValue 转换用于显示的 fileList
watch(
  modelValue,
  (value) => {
    console.log('modelValue-', modelValue);
    value = value || '';
    const arr = value.split(',');
    let ids = arr.filter(
      (item: string) =>
        item &&
        item !== 'null' &&
        item !== 'undefined' &&
        !fileList.value.find((f: any) => f.__fileId == item)
    );

    if (!ids.length) {
      return;
    }
    FileAPI.getBatchFileList(ids).then((data) => {
      fileList.value = fileList.value.concat(
        data.map((item: any) => {
          return {
            __fileId: item.id,
            uid: item.id,
            name: item.original, // fileName
            type: item.type,
          };
        })
      );
    });
  },
  {
    immediate: true,
  }
);

const handleChange = (file: any, fileList: any) => {
  console.log('file-', file);
  console.log('fileList-', fileList);
  if (!props.autoUpload) {
    let flag = handleBeforeUpload(file);
    if (!flag) {
      fileList.pop();
      console.log('fileList-', fileList);
    } else {
      file.uid = getUid();
    }
    emit('on-change', fileList);
  }
};

const uploadFilesHttp = async () => {
  console.log('options-', fileList.value);
  for (let i = 0; i < fileList.value.length; i++) {
    const element: any = fileList.value[i];
    const file = element.raw;
    if (!file) {
      continue;
    }
    const formData = new FormData();
    formData.append(props.name, file);
    await FileAPI.create(formData).then((res: any) => {
      element.name = element.raw.name;
      element.type = element.raw.type;
      element.uid = res.id;
      element.__fileId = res.id;
      delete element.raw;
    });
  }
  emit(
    'update:modelValue',
    fileList.value
      .filter((v: any) => v.__fileId)
      .map((v: any) => v.__fileId)
      .join(',')
  );
  props.onSuccess && props.onSuccess(fileList.value);
  return fileList.value.map((item: any) => item.__fileId);
};

/**
 * 上传前校验 文件大小和格式
 */
function handleBeforeUpload(file: any) {
  file = file.raw || file;
  console.log('before-', file);
  const pattern = /^([\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF]|[A-Za-z]|\d|-|_)+\.[A-Za-z]+$/;
  if (!pattern.test(file.name)) {
    ElMessage({
      type: 'error',
      message: '文件名包含特殊字符',
    });
    return false;
  }
  // 限制文件大小
  if (file.size > props.maxFileSize * 1024 * 1024) {
    ElMessage.warning('上传文件不能大于' + props.maxFileSize + 'M');
    return false;
  }
  if (!accept.includes(file.type)) {
    ElMessage({
      type: 'error',
      message: '文件格式不正确',
    });
    return false;
  }
  return true;
}

/*
 * 上传文件
 */
function handleUpload(options: any) {
  console.log('options-', options);
  return new Promise((resolve, reject) => {
    const file: any = options.file;
    const formData = new FormData();
    formData.append(props.name, file);
    FileAPI.create(formData)
      .then((res: any) => {
        setTimeout(() => {
          fileList.value.forEach((item: any) => {
            if (item.raw && item.raw.uid === file.uid) {
              delete item.raw;
              item.__fileId = res.id;
            }
          });
          emit(
            'update:modelValue',
            fileList.value
              .filter((v: any) => v.__fileId)
              .map((v: any) => v.__fileId)
              .join(',')
          );
          props.onSuccess && props.onSuccess(res);
        }, 10);
        resolve(res);
      })
      .catch((error) => {
        reject(error);
      });
  });
}

/**
 * 上传进度
 *
 * @param event
 */
const handleProgress = (event: UploadProgressEvent) => {
  progressPercent.value = event.percent;
};

/**
 * 上传成功
 */
const handleSuccess = (response: any, uploadFile: UploadFile, files: UploadFiles) => {
  ElMessage.success('上传成功');
  //只有当状态为success或者fail，代表文件上传全部完成了，失败也算完成
  if (
    files.every((file: UploadFile) => {
      return file.status === 'success' || file.status === 'fail';
    })
  ) {
    let fileInfos = [] as any[];
    files.map((file: any) => {
      if (file.status === 'success') {
        //只取携带response的才是刚上传的
        let res = file.response;
        if (res) {
          fileInfos.push(file.__fileId);
        }
      } else {
        //失败上传 从fileList删掉，不展示
        fileList.value.splice(
          fileList.value.findIndex((e: any) => e.uid === file.uid),
          1
        );
      }
    });
    emit('update:modelValue', fileInfos.join(','));
  }
};

/**
 * 上传失败
 */
const handleError = (_error: any) => {
  console.error(_error);
  ElMessage.error('上传失败');
};

/**
 * 删除文件
 */
function handleRemove(file: any) {
  if (file.__fileId) {
    return FileAPI.deleteByIds([file.__fileId]).then(() => {
      console.log('fileList-', fileList.value);

      fileList.value.splice(
        fileList.value.findIndex((e: any) => e.__fileId === file.__fileId),
        1
      );
      emit(
        'update:modelValue',
        fileList.value
          .filter((v: any) => v.__fileId)
          .map((v: any) => v.__fileId)
          .join(',')
      );
      emit('remove', file);
    });
  } else {
    fileList.value.splice(
      fileList.value.findIndex((e: any) => e.uid === file.uid),
      1
    );
    return new Promise(() => {});
  }
}

/**
 * 下载文件
 */
function handleDownload(file: any) {
  const { url, name } = file;
  if (url) {
    FileAPI.downloadFile(file.__fileId, name);
  }
}

/** 获取一个不重复的id */
function getUid(): number {
  // 时间戳左移13位（相当于乘以8192） + 4位随机数
  return (Date.now() << 13) | Math.floor(Math.random() * 8192);
}

defineExpose({
  uploadFilesHttp,
  remove: handleRemove,
  download: handleDownload,
});
</script>

<style lang="scss" scoped>
.el-upload-list__item .el-icon--close {
  position: absolute;
  top: 50%;
  right: 5px;
  color: var(--el-text-color-regular);
  cursor: pointer;
  opacity: 0.75;
  transform: translateY(-50%);
  transition: opacity var(--el-transition-duration);
}

:deep(.el-upload-list) {
  margin: 0;
}

:deep(.el-upload-list__item) {
  margin: 0;
}
</style>
